package com.gulimall.seckill.service.impl;

import com.alibaba.fastjson.JSON;
import com.guigui.common.to.MemberDto;
import com.guigui.common.to.SkuInfoTo;
import com.gulimall.seckill.feign.CouponFeignService;
import com.gulimall.seckill.feign.ProductFeignService;
import com.gulimall.seckill.interceptor.GulimallAuthInterceptor;
import com.gulimall.seckill.service.SecKillService;
import com.gulimall.seckill.to.*;
import org.redisson.api.RSemaphore;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
public class SecKillServiceImpl implements SecKillService {

    private static final String KILL_PROMOTION_PREFIX = "seckill:promotion:set";

    private static final String KILL_LIMIT_PREFIX = "seckill:limit:";

    @Autowired
    private CouponFeignService couponFeignService;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void upLoadSecKillProduct() {

        List<SeckillPromotionTo> seckillSessionLatest3Days = couponFeignService.getSeckillSessionLatest3Days();
        seckillSessionLatest3Days.forEach(this::upLoadSecKillProduct);
    }

    @Override
    public List<RedisCacheSkuInfoTo> getSecKillProduct() {
        List<RedisCacheSkuInfoTo> res = new ArrayList<>();
        Set<String> members = stringRedisTemplate.opsForSet().members(KILL_PROMOTION_PREFIX);
        if (members != null && members.size() > 0) {
            for (String promotion : members) {
                String[] split = promotion.split("_");
                long now = new Date().getTime();
                long start = Long.parseLong(split[0]);
                long end = Long.parseLong(split[1]);
                String promotionId = split[2];

                if (start <= now && now <= end) {
                    // 符合时间
                    ListOperations<String, String> opsForList = stringRedisTemplate.opsForList();
                    List<String> range = opsForList.range(getKILL_SESSION_PREFIX(promotionId), 0, 100);
                    res.addAll(getSkusBySessions(range, promotionId));
                }
            }
        }
        return res;
    }

    public List<RedisCacheSkuInfoTo> getSkusBySessions(List<String> range, String promotionId) {
        List<RedisCacheSkuInfoTo> res = new ArrayList<>();
        for (String sessionKey : range) {
            String[] split = sessionKey.split("_");
            long now = new Date().getTime();
            long start = Long.parseLong(split[0]);
            long end = Long.parseLong(split[1]);
            String hashKey = split[2] + "_" + split[3];
            if (start <= now && now <= end) {
                String json = (String) stringRedisTemplate.opsForHash().get(getKILL_SKUS_PREFIX(), hashKey);
                RedisCacheSkuInfoTo redisCacheSkuInfoTo = JSON.toJavaObject(JSON.parseObject(json), RedisCacheSkuInfoTo.class);
                res.add(redisCacheSkuInfoTo);
            }
        }
        return res;
    }

    @Override
    public RedisCacheSkuInfoTo getKillDetailBySkuId(Long skuId) {
        BoundHashOperations<String, String, String> hashOps = stringRedisTemplate.boundHashOps(getKILL_SKUS_PREFIX());
        Set<String> keys = hashOps.keys();
        if (keys != null && keys.size() > 0) {
            for (String key : keys) {
                String[] split = key.split("_");
                if (skuId.toString().equals(split[0])) {
                    String json = hashOps.get(key);
                    RedisCacheSkuInfoTo redisCacheSkuInfoTo = JSON.toJavaObject(JSON.parseObject(json), RedisCacheSkuInfoTo.class);
                    long now = new Date().getTime();
                    long start = redisCacheSkuInfoTo.getStartTime();
                    long end = redisCacheSkuInfoTo.getEndTime();
                    if (start <= now && now <= end) {
                        return redisCacheSkuInfoTo;
                    }
                }
            }
        }
        return null;
    }

    @Override
    public String kill(Long skuId, String token, Long sessionId, Long promotionId) throws InterruptedException {
        // seckill:promotion:2:sessions:stock:4227c5434ee1472c9b5de1ef1870a1d1 lockKey

        String kill_semaphore_prefix = getKILL_SEMAPHORE_PREFIX(String.valueOf(promotionId)) + token;
        RSemaphore semaphore = redissonClient.getSemaphore(kill_semaphore_prefix);
        MemberDto memberDto = GulimallAuthInterceptor.memberThreadLocal.get();


        String limitKey = KILL_LIMIT_PREFIX + token + "_" + sessionId + "_" + promotionId + "_" + skuId + memberDto.getId();
        Boolean bought = stringRedisTemplate.opsForValue().setIfAbsent(limitKey, "ddddddd", 10, TimeUnit.MINUTES);
        if (semaphore.isExists() && Boolean.TRUE.equals(bought)) {
            if (semaphore.tryAcquire()) {

                String orderSn = UUID.randomUUID().toString().replace("-", "");
                SeckillOrderTo seckillOrderTo =
                        new SeckillOrderTo()
                                .setOrderSn(orderSn)
                                .setSecKillCount(1)
                                .setSecKillPrice(new BigDecimal(1))
                                .setCreateTime(System.currentTimeMillis())
                                .setSessionId(sessionId).setPromotionId(promotionId).setSkuId(skuId)
                                .setUserId(memberDto.getId());


                rabbitTemplate.convertAndSend("seckill-event-exchange", "seckill.order.success", seckillOrderTo);
                return orderSn;
            }
        }
        return null;
    }


    //
    public void upLoadSecKillProduct(SeckillPromotionTo seckillPromotion) {
        String promotionId = seckillPromotion.getId().toString();
        BoundHashOperations<String, Object, Object> hashOps = stringRedisTemplate.boundHashOps(getKILL_SKUS_PREFIX());
        long time = seckillPromotion.getStartTime().getTime();
        long time1 = seckillPromotion.getEndTime().getTime();
        String val = time + "_" + time1 + "_" + promotionId;
        SetOperations<String, String> opsForSet = stringRedisTemplate.opsForSet();
        Boolean member = opsForSet.isMember(KILL_PROMOTION_PREFIX, val);

        if (!Boolean.TRUE.equals(member)) {
            opsForSet.add(KILL_PROMOTION_PREFIX, val);
            List<SeckillSessionTo> seckillSessions = seckillPromotion.getSeckillSessions();
            for (SeckillSessionTo session : seckillSessions) {
                String token = UUID.randomUUID().toString().replace("-", "");

                SeckillSkuRelationTo relation = session.getSeckillSkuRelation();
                SkuInfoTo skuInfoBySkuId = productFeignService.getSkuInfoBySkuId(relation.getSkuId());

                // 活动信息 list保存
                long startTime = session.getStartTime().getTime();
                long endTime = session.getEndTime().getTime();
                stringRedisTemplate.opsForList().leftPush(getKILL_SESSION_PREFIX(promotionId), startTime + "_" + endTime + "_" + skuInfoBySkuId.getSkuId() + "_" + session.getId());

                // 该活动商品信息 hash保存
                RedisCacheSkuInfoTo redisCacheSkuInfoTo = new RedisCacheSkuInfoTo();
                BeanUtils.copyProperties(relation, redisCacheSkuInfoTo);
                redisCacheSkuInfoTo.setSkuInfoTo(skuInfoBySkuId);
                redisCacheSkuInfoTo.setStartTime(session.getStartTime().getTime());
                redisCacheSkuInfoTo.setEndTime(session.getEndTime().getTime());
                redisCacheSkuInfoTo.setToken(token);
                hashOps.put(skuInfoBySkuId.getSkuId() + "_" + session.getId(), JSON.toJSONString(redisCacheSkuInfoTo));


                RSemaphore semaphore = redissonClient.getSemaphore(getKILL_SEMAPHORE_PREFIX(promotionId) + token);
                semaphore.trySetPermits(relation.getSeckillCount().intValue());
            }
        } else {
            System.out.println("当天活动已上架" + seckillPromotion);
        }


    }


    public String getKILL_SESSION_PREFIX(String promotionId) {
        return "seckill:promotion:" + promotionId + ":sessions:session";
    }

    public String getKILL_SKUS_PREFIX() {
        return "seckill:promotion:sessions:skus";
    }

    public String getKILL_SEMAPHORE_PREFIX(String promotionId) {
        return "seckill:promotion:" + promotionId + ":sessions:stock:";
    }


}
